<?php

namespace app\common\controller;

use \think\Db;

/**
 * RESTFul API公共的方法调用
 * 统一的返回格式为:
 * {
 *   "data": [],        //表示执行结果
 *   "errmsg": null,    //可能不存在
 *   "status": 200      //表示执行结果
 * }
 *
 */
class ApiCommon extends BaseController
{
    /**
     * 此接口相关联的模型的名称
     * @var string
     */
    protected $modelName = null;

    /**
     * 根据$modelName的定义,获取一个相关联的模型
     *
     * @return void
     */
    protected function _getModel()
    {
        if ($this->modelName) {
            return model($this->modelName);
        } else {
            throw new \Exception('未为RESTFul控制器"' . get_called_class() . '"定义模型名称modelName');
        }
    }

    /**
     * RESTFul 查询接口核心代码
     * @param array $filter 过滤器,如果值为null,则表示自动从request中获取,其格式如:[['field1','op','value1'],['field','op','value2']...]
     * @param array $visible 需要返回的字段的名称,其格式如:['field1','field2']
     * @param array $order 排序参数,其格式为:['field'=>'filed1','desc'=>false]
     * @param array $page 分页参数,其格式为:['limit'=>'10','current'=>0]
     * @param bool $rawReturn 是否直接返回原始数据(用于在查询完成时进行进一步的处理)
     * @return \think\Response|array 返回响应或查询到的数据
     */
    protected function _index(array $filter = null, array $visible = null, array $order = null, array $page = null, bool $rawReturn = false)
    {
        $request = $this->request;
        if ($filter === null && $filter = $request->param('filter')) {
            $filter = json_decode(urldecode($filter));
        }

        if ($visible === null && $visible = $request->param('visible')) {
            $visible = json_decode(urldecode($visible));
        }
        if ($order === null && $order = $request->param('order')) {
            $order = json_decode(urldecode($order));
        }

        if ($page === null && $page = $request->param('page')) {
            $page = json_decode(urldecode($page));
        }

        $model = $this->_getModel();

        if ($filter !== null) {
            foreach ($filter as &$v) {
                if ($v[1] === 'exp') {
                    $v[2] = Db::raw($v[2]);
                }
            }
            $query = $model->where($filter);
        } else {
            $query = $model->db();
        }
        if ($visible !== null) {
            $query->visible($visible);
        }
        if ($order != null) {
            $query->order($order['field'], $order['desc'] ? "desc" : "asc");
        }

        if ($page !== null) {
            $query->page($page['current'], $page['limit']);
        }
        $ret = $query->select();
        if ($rawReturn) {
            return $ret->toArray();
        } else {
            return json_response($ret->toArray());
        }
    }


    /**
     * RESTFul 读取单个数据核心代码
     *
     * @param mixed $id 要读取的数据的主键数据
     * @return \think\Response 返回查询到的单个数据
     */
    protected function _read($id)
    {
        $model = $this->_getModel();
        if ($ret = $model->get($id)) {
            return \json_response([$ret]);
        } else {
            return \json_response([], STATUS_NOT_FOUND, "找不到指定数据");
        }
    }

    /**
     * RESTful 保存单个数据核心代码
     * @param mixed $data 要提交的数据如果为null,则自动从post请求的body中获取
     * @param array|true $allowField 允许更新的字段的名称,用于限制更新不该写入的字段,如果为true,则表示表中的所有字段都允许更新
     * @param bool $rawReturn 是否直接返回结果而不生成Response,用于需要根据结果进行其他操作时使用
     * @return \think\Response|false|mixed 返回新增的数据的主键ID,如果rawReturn为true,会返回主键的值或false
     */
    protected function _save($data = null, $allowField = true, bool $rawReturn = false)
    {
        if ($data === null) {
            if (!($data = $this->request->post())) {
                return json_response([], STATUS_INVALD_PARAM, "必须提交数据");
            }
        }
        $model = $this->_getModel();
        try {
            if ($model->allowField($allowField)->isUpdate(false)->save($data)) {
                $model= $model->get($model->getAttr($model->getPk()));
                if ($rawReturn) {
                    return $model->getAttr($model->getPk());
                } else {
                    return json_response([$model->getAttr($model->getPk())]);
                }
            } else {
                if ($rawReturn) {
                    return false;
                } else {
                    return json_response([], STATUS_FAIL, "保存数据失败");
                }
            }
        } catch (\Exception $err) {
            if ($rawReturn) {
                return false;
            } else {
                return json_response([], STATUS_FAIL, $err->getMessage());
            }
        }
    }

    /**
     * RESTFul 保存多个数据
     * @param $data 需要更新的数据,形式如:[[数据1],[数据2]...]
     * @param array|true $allowField 允许更新的字段的名称,用于限制更新不该写入的字段,如果为true,则表示表中的所有字段都允许更新
     * @param bool $rawReturn 是否直接返回结果而不生成Response,用于需要根据结果进行其他操作时使用
     * @return \think\Response|bool 返回是否成功的响应,对于Response的响应,其data字段一定为[],请通过状态码来识别是否更新成功
     */
    protected function _saves(array $data = null, $allowField = true, bool $rawReturn = false)
    {
        if ($data === null) {
            $data = $this->request->post();
        }
        $model = $this->_getModel();

        try {
            $model->allowField($allowField)->saveAll($data, false);
            if ($rawReturn) {
                return true;
            } else {
                return \json_response([]);
            }
        } catch (\Exception $err) {
            if ($rawReturn) {
                return false;
            } else {
                return \json_response([], STATUS_FAIL, $err->getMessage());
            }
        }
    }


    /**
     * RESTFul 保存一个列表(此操作会同步整个表,不推荐用在使用自增主键的模型上,这会导致自增主键不断递增)
     * @param array $where 用于筛选出所有待更新的数据的查询条件,其语法与_index()相同
     * @param array $data 待更新的数据,其语法与_save()相同,如果传入的数据为[],则会清空符合条件的列表
     * @param array|true $allowField 允许更新的字段的名称,用于限制更新不该写入的字段,如果为true,则表示表中的所有字段都允许更新
     * @param bool $rawReturn 是否直接返回结果而不生成Response,用于需要根据结果进行其他操作时使用
     * @return \think\Response|bool 返回是否成功的响应,对于Response的响应,其data字段一定为[],请通过状态码来识别是否更新成功
     */
    protected function _saveList(array $where = null, array $data = null, $allowField = true, bool $rawReturn = false)
    {
        $request = $this->request;
        if ($where === null) {
            $where = $request->param('where');
        }
        if (!$where) {
            return \json_response([], STATUS_INVALD_PARAM, "where参数不能为空");
        }
        if ($data == null) {
            $data = $request->param('data', []);
        }

        $model = $this->_getModel();

        $model->startTrans();
        try {
            //删除旧数据
            $model->where($where)->delete();
            if ($pk = $model->getPk()) { //去掉主键为0的值
                foreach ($data as &$v) {
                    if (isset($v[$pk]) && !$v[$pk]) {
                        unset($v[$pk]);
                    }
                }
            }

            //新增数据,从而达到更新的目的
            $class = new \ReflectionClass(model($this->modelName));
            foreach ($data as $d) {
                $m = $class->newInstance();
                $m->isUpdate(false)->allowField($allowField)->save($d);
            }
            $model->commit();

            if ($rawReturn) {
                return true;
            } else {
                return json_response([]);
            }
        } catch (\Exception $err) {
            $model->rollback();
            if ($rawReturn) {
                return false;
            } else {
                json_response([], STATUS_FAIL, $err->getMessage());
            }
        }
    }


    /**
     * 更新单个数据
     *
     * @param mixed $id 待更新的数据的主键
     * @param mixed $data 需要被更新的键值对(可以不用包含所有字段)
     * @param array|true $allowField 允许更新的字段的名称,用于限制更新不该写入的字段,如果为true,则表示表中的所有字段都允许更新
     * @param bool $rawReturn 是否直接返回结果而不生成Response,用于需要根据结果进行其他操作时使用
     * @return void
     */
    protected function _update($id, array $data = null, $allowField = true, bool $rawReturn = false)
    {
        if ($data == null) {
            $data = $this->request->put();
        }

        $model = $this->_getModel();
        try {
            if ($model->allowField($allowField)->isUpdate(true)->save($data, [$model->getPk() => $id])) {
                if ($rawReturn) {
                    return true;
                } else {
                    return json_response([]);
                }
            } else {
                if ($rawReturn) {
                    return false;
                } else {
                    return json_response([], STATUS_FAIL, "保存数据失败");
                }
            }
        } catch (\Exception $err) {
            if ($rawReturn) {
                return false;
            } else {
                return json_response([], STATUS_FAIL, $err->getMessage());
            }
        }
    }

    /**
     * 更新多个数据
     * @param mixed $data 需要被更新的键值对(可以不用包含所有字段,但必须包含ID字段)
     * @param array|true $allowField 允许更新的字段的名称,用于限制更新不该写入的字段,如果为true,则表示表中的所有字段都允许更新
     * @param bool $rawReturn 是否直接返回结果而不生成Response,用于需要根据结果进行其他操作时使用
     */
    protected function _updates(array $data = null, $allowField = true, bool $rawReturn = false)
    {
        if ($data == null) {
            $data = $this->request->put();
        }
        $model = $this->_getModel();
        $pk = $model->getPk();
        foreach ($data as $v) {
            if (!isset($v[$pk])) {
                if ($rawReturn) {
                    return false;
                } else {
                    return json_response([], STATUS_INVALD_PARAM, "批量更新的数据中必须包含主键");
                }
            }
        }


        try {
            $model->allowField($allowField)->saveAll($data, true);
            if ($rawReturn) {
                return true;
            } else {
                return json_response([]);
            }
        } catch (\Exception $err) {
            if ($rawReturn) {
                return false;
            } else {
                return json_response([], STATUS_FAIL, $err->getMessage());
            }
        }
    }



    /**
     * 删除单个数据
     * 
     * @param mixed $id 待更新的数据的主键值
     * @param bool $rawReturn 是否直接返回结果而不生成Response,用于需要根据结果进行其他操作时使用
     * @return 
     */
    protected function _delete($id, bool $rawReturn = false)
    {
        $model = $this->_getModel();
        if ($ret = $model->get($id)) {
            try {
                $ret->delete();
                if ($rawReturn) {
                    return true;
                } else {
                    return json_response([]);
                }
            } catch (\Exception $err) {
                if ($rawReturn) {
                    return false;
                } else {
                    return json_response([], STATUS_FAIL, $err->getMessage());
                }
            }
        } else {
            if ($rawReturn) {
                return false;
            } else {
                return json_response([], STATUS_NOT_FOUND, "没有指定数据");
            }
        }
    }

    /**
     * 删除多个数据
     * 
     * @param bool $rawReturn 是否直接返回结果而不生成Response,用于需要根据结果进行其他操作时使用
     * @return void
     */
    protected function _deletes($ids, bool $rawReturn = false)
    {
        $model = $this->_getModel();

        if ($ret = $model->select($ids)) {
            try {
                $model->startTrans();
                foreach ($ret as $v) {
                    $v->delete();
                }
                $model->commit();
                if ($rawReturn) {
                    return true;
                } else {
                    return json_response([]);
                }
            } catch (\Exception $err) {
                $model->rollback();
                if ($rawReturn) {
                    return false;
                } else {
                    return json_response([], STATUS_FAIL, $err->getMessage());
                }
            }
        } else {
            if ($rawReturn) {
                return false;
            } else {
                return json_response([], STATUS_NOT_FOUND, "找不到指定数据");
            }
        }
    }
}
